﻿using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Singer.Core;
using Singer.Middleware.Redis;
using StackExchange.Redis;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace Singer.Middleware.JwtAuth;

/// <summary>
/// JwtToken服务
/// </summary>
public class JwtAuthTokenManager : IJwtAuthTokenManager
{
    public JwtOptions JwtAuthOptions => Options;
    private readonly JwtOptions Options;
    IRedisConnectionPool _redisConnectionPool;
    public JwtAuthTokenManager(IRedisConnectionPool redisConnectionPool, IOptions<JwtOptions> options)
    {
        _redisConnectionPool = redisConnectionPool;
        if (options.Value == null)
            throw new Exception("【JwtAuth】Config was not found.");
        options.Value.Validate();
        Options = options.Value;
    }

    public async Task<string> CreateTokenAsync(IEnumerable<Claim> claims)
    {
        //对称可逆加密 秘钥
        SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Options.SecurityKey));
        SigningCredentials sign = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        DateTime expirationTime = Cores.BeiJingNow.AddHours(Options.ExpirationHour);
        JwtSecurityToken securityToken = new JwtSecurityToken(
         issuer: Options.Issuer,
         audience: Options.Audience,
         claims: claims,
         expires: expirationTime,//token过期时间
         signingCredentials: sign);
        string token = new JwtSecurityTokenHandler().WriteToken(securityToken);
        //计算token的redisKey jwttokens:{MD5加密后的token}
        string tokenKey = JwtOptions.GetTokenCacheKey(token);
        //将token信息存redis
        IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
        await db.StringSetAsync(tokenKey, token, TimeSpan.FromHours(Options.ExpirationHour));
        return token;
    }

    public async Task DeleteTokenAsync(string token)
    {
        string tokenKey = JwtOptions.GetTokenCacheKey(token);
        IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
        await db.KeyDeleteAsync(tokenKey);
    }

    public Task<bool> ExistsTokenAsync(string token)
    {
        string tokenKey = JwtOptions.GetTokenCacheKey(token);
        IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
        return db.KeyExistsAsync(tokenKey);
    }

    
}
